package com.retry.task.core.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

/**
 * @author gao.gwq
 * @version 1.0
 * @email: gao.gwq@alibaba-inc.com
 * @date 2020/1/15 4:11 下午
 * @Description: TODO 公共的方法
 */
public class CommonExecuteUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(CommonExecuteUtils.class);

    private CommonExecuteUtils() {
    }

    /**
     * 方法多次重试
     *
     * @param function    具体执行方法
     * @param checkResult 校验结果是否成功 返回boolean ，返回true 为成功
     * @param tryTimes    尝试次数
     * @param intervalMS  睡眠时间 毫秒
     * @param <T>
     * @param <R>
     * @return
     * @throws Exception
     */
    public static <T, R> R executeWithRetry(Function<T, R> function,
        Function<R, Boolean> checkResult,
        int tryTimes, long intervalMS) {
        return executeWithRetry(function, checkResult, TimeUnit.MILLISECONDS, tryTimes, intervalMS);
    }

    public static <R, T> List<R> transferVO(List<?> sourceList, Class<R> clazz) {
        List<R> resultList = Lists.newArrayList();
        try {
            for (Object source : sourceList) {
                R r = clazz.newInstance();
                BeanUtils.copyProperties(source, r);
                resultList.add(r);
            }
        } catch (Exception ex) {
            LOGGER.error("class newinstance error", ex);
            throw new RuntimeException(ex);
        }
        return resultList;
    }

    /**
     * @param function
     * @param checkResult 返回boolean ，返回true 为成功
     * @param timeUnit
     * @param tryTimes
     * @param intervalMS
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> R executeWithRetry(Function<T, R> function,
        Function<R, Boolean> checkResult,
        TimeUnit timeUnit,
        int tryTimes, long intervalMS) {
        if (intervalMS <= 0) {
            throw new IllegalArgumentException("intervalMS can not be litter than 0");
        }
        if (tryTimes <= 0) {
            return function.apply(null);
        }
        for (int i = 1; i < tryTimes; i++) {
            try {
                R r = function.apply(null);
                if (checkResult == null) {
                    return r;
                }
                Boolean flag = checkResult.apply(r);
                if (flag == true) {
                    return r;
                }
                timeUnit.sleep(intervalMS);
            } catch (Exception ex) {
                LOGGER.error("执行任务错误，错误原因:{},", ex.getMessage(), ex);
                try {
                    timeUnit.sleep(intervalMS);
                } catch (Exception ex1) {

                }
            }
        }
        return function.apply(null);
    }

    /**
     * 获取全部的数据
     *
     * @param startPage
     * @param limit
     * @param function
     * @param <T>
     * @return
     */
    public static <T> List<T> findByQuery(Integer startPage, Integer limit,
        BiFunction<Integer, Integer, List<T>> function) {
        if (startPage <= 0) {
            throw new IllegalArgumentException("startPage is error");
        }
        if (limit <= 0) {
            throw new IllegalArgumentException("limit is error");
        }
        if (function == null) {
            throw new IllegalArgumentException("function is null");
        }
        List<T> list = new ArrayList<>(16);
        while (true) {
            List<T> result = function.apply(startPage, limit);
            if (result == null) {
                break;
            }
            if (result.size() == 0) {
                break;
            }
            list.addAll(result);
            if (result.size() < limit) {
                break;
            }
            startPage++;
        }
        return list;
    }

    public static <T> void batchExecute(Collection<T> elementList, Integer pageSize, Consumer<List<T>> consumer) {
        if (CollectionUtils.isEmpty(elementList)) {
            return;
        }
        int tick = 0;
        if (pageSize == null || pageSize == 0) {
            pageSize = 500;
        }
        List<T> cloneList = new ArrayList<>();
        for (T temp : elementList) {
            cloneList.add(temp);
            tick++;
            if (tick % pageSize == 0) {
                consumer.accept(cloneList);
                cloneList.clear();
            }
        }
        if (!CollectionUtils.isEmpty(cloneList)) {
            consumer.accept(cloneList);
            cloneList.clear();
        }
    }

    /**
     * 对list，根据某个属性进行去重
     *
     * @param cases
     * @param comparator
     * @param <T>
     * @return
     */
    public static <T> List<T> removeDuplicateCase(List<T> cases, Comparator<T> comparator) {
        Set<T> set = new TreeSet<>(comparator);
        set.addAll(cases);
        return new ArrayList<>(set);
    }

    public static void main(String[] args) {
    }

    public static Map<String, Object> beanToMap(Object obj, boolean nullParamFlag) {
        if (obj == null) {
            return null;
        }

        Map<String, Object> paramMap = Maps.newHashMap();
        List<Field> fieldList = Lists.newArrayList();
        Class clazz = obj.getClass();
        while (true) {
            if (clazz == null || clazz.equals(Object.class)) {
                break;
            }
            Field[] fieldArr = clazz.getDeclaredFields();
            fieldList.addAll(Lists.newArrayList(fieldArr));
            clazz = clazz.getSuperclass();
        }

        if (fieldList.size() == 0) {
            return paramMap;
        }
        for (Field field : fieldList) {
            try {
                field.setAccessible(true);
                Object object = field.get(obj);
                if (nullParamFlag == true) {
                    paramMap.put(field.getName(), object);
                } else {
                    if (object != null) {
                        paramMap.put(field.getName(), object);
                    }
                }
            } catch (Exception ex) {

            }
        }
        return paramMap;
    }


    /**
     * list 转为 另外一个list
     *
     * @param list
     * @param function
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<T> listToList(List<R> list, Function<R, T> function) {
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        List<T> newList = Lists.newArrayList();
        for (R r : list) {
            T t = function.apply(r);
            newList.add(t);
        }
        return newList;
    }

    /**
     * list 转 map
     *
     * @param list
     * @param function
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> function) {
        Map<K, V> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(list)) {
            return map;
        }
        for (V v : list) {
            K key = function.apply(v);
            map.put(key, v);
        }
        return map;
    }

    /**
     * @param list
     * @param keyFunction
     * @param valFunction
     * @param <K>
     * @param <V>
     * @param <R>
     * @return
     */
    public static <K, V, R> Map<K, R> listToMap(List<V> list, Function<V, K> keyFunction, Function<V, R> valFunction) {
        Map<K, R> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(list) || keyFunction == null || valFunction == null) {
            return map;
        }
        for (V v : list) {
            K key = keyFunction.apply(v);
            R value = valFunction.apply(v);
            if (key == null || value == null) {
                continue;
            }
            map.put(key, value);
        }
        return map;
    }

    public static <K, V, R> Map<K, List<R>> listToMapList(List<V> list, Function<V, K> keyFunction,
        Function<V, R> valFunction) {
        Map<K, List<R>> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(list) || keyFunction == null || valFunction == null) {
            return map;
        }
        for (V v : list) {
            K key = keyFunction.apply(v);
            List<R> rList = map.get(key);
            R value = valFunction.apply(v);
            if (rList == null) {
                rList = Lists.newArrayList();
            }
            if (key == null || value == null) {
                continue;
            }
            rList.add(value);
            map.put(key, rList);
        }
        return map;
    }

    public static <K, V, R> Map<K, List<R>> listToMapList(List<V> list, Map<K, List<R>> map, Function<V, K> keyFunction,
        Function<V, R> valFunction) {
        if (CollectionUtils.isEmpty(list) || keyFunction == null || valFunction == null) {
            return map;
        }
        for (V v : list) {
            K key = keyFunction.apply(v);
            List<R> rList = map.get(key);
            R value = valFunction.apply(v);
            if (rList == null) {
                rList = Lists.newArrayList();
            }
            if (key == null || value == null) {
                continue;
            }
            rList.add(value);
            map.put(key, rList);
        }
        return map;
    }

    public static <K, V> Map<K, V> sortMapByKey(Map<K, V> map, Comparator<K> comparator) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map<K, V> sortMap = new TreeMap<K, V>(comparator);
        sortMap.putAll(map);
        return sortMap;
    }

    public static Integer findSubStrNum(String srcStr, String subStr) {
        if (StringUtils.isEmpty(srcStr) || StringUtils.isEmpty(subStr)) {
            return -1;
        }
        int count = 0;
        int srcLength = srcStr.length();
        int subLength = subStr.length();
        loop1:
        for (int i = 0; i < srcLength; i++) {
            Character srcChar = srcStr.charAt(i);
            Character subChar = subStr.charAt(0);
            if (srcChar.equals(subChar)) {
                boolean flag = true;
                loop2:
                for (int j = 1; j < subLength; j++) {
                    srcChar = srcStr.charAt(i + j);
                    subChar = subStr.charAt(j);
                    if (!srcChar.equals(subChar)) {
                        flag = false;
                        break loop2;
                    }
                }
                if (flag == true) {
                    count++;
                    i = i + subLength - 1;
                }
            }
        }
        return count;
    }

    public static <T, R> List<CompletableFuture> currentExecute(Collection<T> list, Integer size,
        Function<List<T>, R> function, ThreadPoolExecutor threadPoolExecutor) {
        if (size <= 0) {
            throw new IllegalArgumentException("size can not littler 0");
        }
        List<CompletableFuture> futureList = Lists.newArrayList();
        CommonExecuteUtils.batchExecute(list, size, subList -> {
            List<T> cloneList = Lists.newArrayList(subList);
            CompletableFuture future = CompletableFuture.supplyAsync(() -> {
                return function.apply(cloneList);
            }, threadPoolExecutor).exceptionally(ex -> {
                throw new RuntimeException(ex);
            });
            futureList.add(future);
        });
        return futureList;
    }

    public static String upperCharacter(String str, int start, int end) {
        if (str == null || str.trim().length() == 0) {
            throw new IllegalArgumentException("str is null");
        }
        if (start < 0) {
            throw new IllegalArgumentException("start is null");
        }
        if (end < start) {
            throw new IllegalArgumentException("end is litter than start size");
        }
        int length = str.length();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i >= start && i <= end) {
                stringBuilder.append(Character.toUpperCase(str.charAt(i)));
                continue;
            }
            stringBuilder.append(str.charAt(i));
        }
        return stringBuilder.toString();
    }
}
